#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import math
from dataclasses import dataclass

from dws.commons.common import check_sh_cmd_param, check_sql_cmd_param, log
from common.util.cmd_utils import cmd_format
from dws.resource.dws_common import DwsCommon

FILTER_SYS_SCHEMA = "WHERE (oid > 16384 or oid = 2200) and nspname != 'scheduler' and nspname != 'pmk'"
START_FLAG = "nspname"
CNT_START_FLAG = "count"
INVALID_COUNT = -1
DEFAULT_PAGE_SIZE = 100


@dataclass
class DwsSchemaParam:
    user_name: str
    env_file: str
    db_name: str
    condition: dict
    pid: str = ""


class DwsSchema:

    def __init__(self, schema_param):
        self._original_schema_lines = []
        self._user_name = schema_param.user_name
        self._env_file = schema_param.env_file
        self._db_name = schema_param.db_name
        self._pid = schema_param.pid
        self._page_no = 0
        self._page_size = DEFAULT_PAGE_SIZE
        self._conditions = schema_param.condition.get("conditions", {})
        self._condition_dict = {"schemaName": "nspname"}
        self._total_count = INVALID_COUNT
        self._total_page = INVALID_COUNT
        self._query_success = False
        self._query_schemas(schema_param.condition)

    def is_query_success(self):
        return self._query_success

    def get_all_schemas(self):
        """
        从字符串中筛选出所有的schema名称
        """
        schemas = []
        if not self._query_success:
            log.error(f"Query success flag is false, PID: {self._pid}.")
            return schemas
        start_index = -1
        end_index = len(self._original_schema_lines) - 1
        for index, cur_line in enumerate(self._original_schema_lines):
            if START_FLAG in cur_line:
                start_index = index + 2
                break
        if start_index == -1:
            log.error(f"Cannot find start flage in query schema result, PID: {self._pid}.")
            return schemas

        for index in range(start_index, end_index):
            schema_name = self._original_schema_lines[index].strip()
            if schema_name:
                schemas.append({
                    "type": "",
                    "subType": "",
                    "id": "",
                    "name": f"{self._db_name}/{schema_name}",
                    "parentId": "",
                    "parentName": "",
                    "extendInfo": {}
                })

        return schemas

    def get_total_count(self):
        if self._total_count != INVALID_COUNT:
            return self._total_count
        query_cmd = f"SELECT COUNT(*) FROM pg_namespace {FILTER_SYS_SCHEMA} {self._get_condition_str()}"
        common_obj = DwsCommon(self._user_name, self._env_file, self._pid)
        ret, out_lines = common_obj.exec_gaussdb_sql_cmd(self._db_name, query_cmd)
        if not ret or len(out_lines) < 2:
            log.error(f"Query total count faild, ret: {ret}, length: {len(out_lines)}, PID: {self._pid}.")
            return 0
        count_pos = len(out_lines) - 2
        try:
            self._total_count = int(out_lines[count_pos].strip())
        except Exception:
            log.exception(f"Convert str to int failed, PID: {self._pid}.")
            return 0
        return self._total_count

    def get_total_page(self):
        if self._total_page != INVALID_COUNT:
            return self._total_page
        self.get_total_count()
        try:
            self._total_page = math.ceil(self._total_count / self._page_size)
        except Exception:
            log.exception(f"Get total page failed, PID: {self._pid}.")
            return 0

        return self._total_page

    def _get_condition_str(self):
        condition_str = ""
        if self._conditions:
            for key, value in self._conditions.items():
                if key in self._condition_dict:
                    tmp_key = self._condition_dict.get(key)
                    tmp_key = tmp_key if check_sql_cmd_param(tmp_key) else ""
                    value = value if check_sql_cmd_param(value) else ""
                    condition_str += f"and {tmp_key} ~ '{value}' "

        log.info(f"Query condition [{condition_str}], PID: {self._pid}.")
        return condition_str

    def _query_schemas(self, condition):
        """
        分页查询数据库下面的schema
        """
        str_page_no = condition.get("pageNo", "")
        str_page_size = condition.get("pageSize", "")
        # 检查参数
        try:
            self._page_no = 0 if int(str_page_no) < 0 else int(str_page_no)
        except Exception:
            log.exception(f"Param page_no: {str_page_no} is invalid, PID: {self._pid}.")
            return False
        try:
            self._page_size = DEFAULT_PAGE_SIZE if int(str_page_size) < 1 else int(str_page_size)
        except Exception:
            log.exception(f"Param page_size: {str_page_size} is invalid, PID: {self._pid}.")
            return False
        if not check_sh_cmd_param(self._db_name):
            log.error(f"Param invalid, db_name: {self._db_name}, PID: {self._pid}.")
            return False

        query_cmd = f"SELECT nspname FROM pg_namespace "\
            f"{FILTER_SYS_SCHEMA} {self._get_condition_str()} ORDER BY "\
            f"nspname LIMIT {self._page_size} OFFSET {self._page_no*self._page_size};"
        # 登录对应数据库，分页查询schema
        common_obj = DwsCommon(self._user_name, self._env_file, self._pid)
        self._query_success, self._original_schema_lines = common_obj.exec_gaussdb_sql_cmd(self._db_name, query_cmd)
        if not self._query_success:
            log.error(f"Query schemas faild, page_no: {self._page_no}, page_size: {self._page_size}, PID: {self._pid}.")
            return False

        return True
